Forecasting and simulation ============================ Once a model is solved, the two main entry points for forward-looking analysis are ``simulate`` and ``forecast``. Both return structs of ``ts`` time-series objects keyed by endogenous-variable name and respect the model's regime structure when there is one. For deterministic scenarios, ``perfect_foresight`` is the third entry point. All three consume the **same** :doc:`simulation plan ` -- the universal mechanism for choosing initial conditions, pinning endogenous variables, activating shocks, announcing anticipated shocks, or imposing algebraic constraints. This chapter is the reference for the three consumers. It assumes you have read :doc:`Simulation plans`; the constructions used below are all from that chapter. .. contents:: :local: :depth: 2 ``simulate`` ------------- ``simulate`` propagates the perturbation solution forward through a plan. With no plan it draws an unconditional path:: sim = simulate(m, simul_periods = 200); plot(sim.PAI, sim.Y); With a plan, every choice on that path is the plan's: pinned shocks stay pinned, ``NaN`` shocks are backed out, pinned endogenous variables hit their conditioning values, anticipated shocks act through the perturbation solution's anticipation horizon:: sp = simplan(m, [rq(2020,1), rq(2030,4)], 1, 'shockInit', 'zero'); sp = append(sp, 'EETA', rq(2020,2), 2.0); sim = simulate(m, simul_historical_data = sp); For a single-regime constant-parameter model, ``sim`` is a struct of ``ts`` keyed by endogenous-variable name. For a regime-switching model the output structure depends on ``simul_to_time_series`` (see below). Common options ~~~~~~~~~~~~~~~ .. list-table:: :header-rows: 1 :widths: 35 65 * - Option - Effect * - ``simul_periods`` - Number of periods to simulate. Default ``100``. * - ``simul_burn`` - Burn-in periods discarded from the head of the returned series. Default ``100``. * - ``simul_seed`` - Seed forwarded to MATLAB's RNG before drawing. ``[]`` keeps the current state. * - ``simul_shock_uncertainty`` - When ``true`` (default), draws structural shocks from their declared distributions. When ``false``, sets every shock to its mean and returns the deterministic continuation. * - ``simul_historical_data`` - Initial conditions and / or a conditioning path. Accepts a struct of ``ts`` or a :doc:`simulation plan `. * - ``simul_history_end_date`` - Last date of the history segment (the point from which the simulation continues forward). Inferred from the plan when one is passed. * - ``simul_regime`` - Pin the regime path. Pass an integer for a constant regime, a vector for a fixed path, or a function handle for a state-dependent switch rule. * - ``simul_anticipate_zero`` - When ``true``, agents see only the contemporaneous shock realization -- future draws stay private. Default ``false``. * - ``simul_to_time_series`` - Output shape -- see below. Default ``true``. Output shape ~~~~~~~~~~~~~ By default (``simul_to_time_series = true``) the output is a struct of ``ts`` keyed by variable name. For switching models with multiple parameterizations or solution branches, the underlying numeric representation has extra dimensions for *parameterization* and *regime path*; the ``ts`` object carries them in its pages. When you need the raw numeric arrays -- for instance to feed into a hand-rolled diagnostic -- set ``simul_to_time_series = false``:: [db, info] = simulate(m, ... simul_periods = 200, ... simul_to_time_series = false); ``db`` is then a numeric matrix and ``info`` carries the layout information needed to reconstruct the ``ts`` if you ever want it back. .. note:: Even with ``simul_to_time_series = true``, regime-switching simulations can in some configurations return a numeric matrix rather than a ``ts`` struct (an artefact of the underlying solution shape). Confirm with ``isstruct(sim)`` before reaching for ``sim.varname``; if you got a matrix, the columns are the variables in the order returned by ``get(m, 'endo_list')``. Returning the regime path ~~~~~~~~~~~~~~~~~~~~~~~~~~ ``simulate`` returns the realised composite-regime sequence as its second output:: [sim, states] = simulate(m, simul_periods = 200); ``states`` is a vector of integer regime IDs over the simulation horizon. Plot it to see when each regime was active. Theoretical and sample moments ------------------------------- For *linear* solved models the unconditional first and second moments are available in closed form via the Lyapunov equation, returned by ``theoretical_autocovariances`` and the ``theoretical_autocorrelations`` wrapper:: [Acov, info, retcode] = theoretical_autocovariances(m, 10); Acorr = theoretical_autocorrelations(m, autocorr_ar = 10); For higher-order or regime-switching models, simulate a long path and compute sample moments from the ``ts`` (``mean``, ``var``, ``cov``, ``corrcoef`` are overloaded on ``ts``). The two should agree at order 1 up to Monte Carlo error. The variance-decomposition routine is documented in the legacy chapter *Master stoch simul* and remains canonical for the modern toolbox. Pruned simulations (higher order) --------------------------------- RISE solves and simulates regime-switching DSGE models up to fifth-order perturbation. At order :math:`\geq 2` a straight perturbation simulation is **not guaranteed to be bounded**: the policy is a polynomial in the state, and iterating it forward lets the higher-order terms feed on themselves, so an unlucky shock sequence can send the state off to ``inf``. *Pruning* (Andreasen, Fernández-Villaverde and Rubio-Ramírez, 2018, generalized here to regime switching) removes those spurious higher-order terms at simulation time **without changing the policy function**, keeping the path bounded and the implied moments well defined. Pruning is controlled by the ``simul_pruned`` option, honored by ``simulate``, ``irf``, ``forecast`` and any path that iterates the solution forward: .. code-block:: matlab sim = simulate(m, simul_periods = 200, simul_pruned = true); ``simul_pruned`` accepts: - ``false`` (default) --- no pruning; - ``true`` --- prune with the built-in automatic scheme (``one_step_pruning_automatic``); - the name of a pruning routine (``'one_step_pruning'`` or ``'one_step_pruning_automatic'``), or a function handle, to select a specific scheme. Pruning applies only at order :math:`\geq 2` (at order 1 the recursion is already linear and bounded) and is implemented up to order 5. .. note:: Pruning changes the object you simulate: the pruned recursion is a stabilized companion of the policy, not the policy itself. It is the right tool for moments and long simulations at high order, but it is a modeling choice --- an order-:math:`\geq 2` path that explodes *without* pruning is often telling you that perturbation is being pushed outside its region of validity, where a global method (Taylor projection, :doc:`Extending RISE through paradigms`) may be the better answer. See :doc:`Solution accuracy` for how pruning interacts with the accuracy measures. ``forecast`` ------------- ``forecast`` produces forward paths from the end of history, with or without conditions and with or without shock uncertainty. The unconditional case is the trivial one:: fkst = forecast(m, ... forecast_nsteps = 12, ... forecast_shock_uncertainty = true); The conditional case is where ``forecast`` earns its keep. The conditioning is supplied through a :doc:`simulation plan ` passed in ``simul_historical_data``:: sp = simplan(m, [rq(2020,1), rq(2030,4)], 1, 'shockInit', 'zero'); sp = append(sp, 'ER', cond_dates, NaN); % activate sp = append(sp, 'PAI', cond_dates, PAI_target); % condition fkst = forecast(m, ... simul_historical_data = sp, ... forecast_nsteps = numel(cond_dates) + 4, ... forecast_shock_uncertainty = true); The plan alone carries the conditioning -- the conditioned variables and the freed shocks are read from it, so no separate list of conditioned variables is needed. Two knobs matter at the consumer side: * **Anticipated vs unanticipated** -- per-condition, encoded in the plan's pages. Page ``1`` is contemporaneous (unanticipated); page ``k`` is announced :math:`k-1` periods ahead. The perturbation solution honours the announcement horizon as long as ``anticipationHorizon`` covered it at plan construction. * **Surprises vs no surprises past the conditioning window** -- ``forecast_shock_uncertainty``. ``true`` draws structural shocks past the window; ``false`` zeroes them and returns the deterministic continuation. Hard and soft conditions ~~~~~~~~~~~~~~~~~~~~~~~~~ A **hard condition** pins a variable to a specific value at a specific date. The identification rule (see :doc:`Simulation plans`) determines whether a plan is solvable. A **soft condition** restricts a variable to a band ``[lo hi]`` appended to the plan (see :doc:`Simulation plans` for the band syntax and ``free_shocks``). Because the conditioned variable is moved through the freed shock(s), the model assigns it a predictive distribution at the conditioning date -- a normal :math:`N(a,\sigma^2)` whose mean :math:`a` is the deterministic forecast and whose standard deviation :math:`\sigma` is the freed shock's impulse response. The band *truncates* that distribution. The ``simul_soft_conditioning`` option chooses what is returned: * ``'density'`` (the default once a band is present) runs ``simul_nsim`` plans -- each a draw from the truncated distribution, pinned and solved as a hard condition -- and returns the propagated ensemble. ``fanchart`` then collapses it (below). * ``'mean'`` returns a single path, the conditional mean (the mean of that ensemble). Where the band sits relative to the forecast :math:`a` is what matters. A band that *straddles* :math:`a` is mildly truncated and roughly symmetric. An **off-center** band clips one tail, so the distribution is asymmetric and its mean, its median and the hard pin at the midpoint are three different numbers. A **one-sided** band -- a floor ``[lo NaN]`` -- gives a strongly skewed, zero-lower-bound-style pile-up at the bound. A hard pin discards this; the soft condition keeps the model's view of *where in the band* the variable is likely to sit:: sp = simplan(m, [rq(2020,1), rq(2023,1)], 1, 'shockInit', 'zero'); sp = free_shocks(sp, 'EPS_D', rq(2020,2)); % free the channel sp = append(sp, {'Y', rq(2020,2), [0.10 0.70]}); % an off-center band fkst = forecast(m, simul_historical_data = sp, ... simul_soft_conditioning = 'density', ... simul_nsim = 2000); Setting ``simul_soft_conditioning = 'mean'`` instead returns the single conditional-mean path for the same plan. The option surface ~~~~~~~~~~~~~~~~~~~ .. list-table:: :header-rows: 1 :widths: 35 65 * - Option - Effect * - ``forecast_nsteps`` - Number of forecasting steps (default ``12``). * - ``forecast_start_date`` - Date when the forecasts start (end of history + 1). * - ``simul_soft_conditioning`` - How a band ``[lo hi]`` condition is consumed: ``''`` (the default -- the plan decides, and with no band the problem is hard), ``'density'`` (a propagated distribution / fan), or ``'mean'`` (the conditional-mean path). See `Hard and soft conditions`_. * - ``simul_nsim`` - Number of draws/plans in ``'density'`` soft conditioning (default ``100``; ignored otherwise). * - ``forecast_shock_uncertainty`` - Draw structural shocks over the forecast horizon. Default ``false``. * - ``forecast_to_time_series`` - When ``true`` (default), output is a struct of ``ts``; otherwise a numeric matrix with reconstruction info. * - ``simul_historical_data`` - The history-plus-conditioning database. May be a ``ts`` struct, a struct of ``ts``, or a :doc:`simulation plan `. * - ``simul_history_end_date`` - Last date of the history segment. ``perfect_foresight`` ---------------------- ``perfect_foresight`` solves the full nonlinear model as a boundary-value problem over the plan's horizon. The plan supplies the boundary data; the solver returns a deterministic path consistent with every condition in the plan and with the model equations *at every period*, without the perturbation approximation:: pfs = perfect_foresight(m, simul_historical_data = sp); Because ``perfect_foresight`` and ``simulate`` consume the *same* plan, a side-by-side comparison is the canonical check on the approximation error of the perturbation solution: when the two outputs agree, the deviation is small enough that the nonlinearity does not matter for the scenario; when they diverge, the nonlinear path is the accurate one and the perturbation path is the local approximation. Occasionally-binding constraints are honoured exactly by ``perfect_foresight``; see :doc:`../ModelShapes/DSGE/Occasionally-binding constraints` for the deterministic-OBC patterns. Fan charts ----------- A forecast that carries multiple draws -- the ``'density'`` soft conditioning above, or a stochastic forecast with ``forecast_shock_uncertainty = true`` -- is collapsed into central tendency and probability bands by ``fanchart`` and ``plot_fanchart``:: fkst = forecast(m, simul_historical_data = sp, ... simul_soft_conditioning = 'density', ... simul_nsim = 1000); out = fanchart(fkst.PAI, [30 50 68 90]); plot_fanchart(out, [244 122 66]/255); The percentages are the central probability mass enclosed by each band. The plotting tools live in the legacy chapter *Plotting tools* and apply unchanged to the modern toolbox. Relative-entropy tilting ------------------------- Relative-entropy tilting is a *fourth* mechanism, distinct from the plan-based routes above: it does not produce a new forecast path; it *reweights* an existing forecast ensemble (e.g. the output of ``forecast(..., forecast_shock_uncertainty = true)``) so that its moments satisfy a user-specified constraint. The algorithm and its option surface are documented in the legacy chapter *Conditional forecasting Using Relative Entropy*. Use it when you already have an unconditional ensemble and want to constrain its *moments* (e.g. "the mean inflation forecast over the next 8 quarters equals 2%") rather than its *paths*. Smoother-based conditional forecasts ------------------------------------- For *linear* models with conditions on *observed* variables, the Kalman smoother imputes the conditioning path directly from the data, without a simulation plan -- treat the conditioning values as observations on the forecast horizon, mark every other observable as ``NaN`` for those dates, and run ``filter``. The output is the smoothed path for every endogenous variable. This route is based on least squares and inherits its tradeoffs. It is documented in :doc:`Filtering`; use it when the conditions are observed-side and the model is linear. For anything else -- hard endogenous constraints on unobserved states, nonlinear solutions, regime switching, anticipated shocks, soft conditions -- use a simulation plan. See also --------- * :doc:`Simulation plans` -- the construction reference (this chapter is its consumer side). * :doc:`Filtering` -- the smoother-based alternative for linear observed-side conditioning. * :doc:`../ModelShapes/DSGE/Deterministic and quasi-deterministic solutions` -- the deterministic context in full. * :doc:`../ModelShapes/DSGE/Occasionally-binding constraints` -- scenarios with OBC. * :doc:`Solution accuracy` -- how pruning interacts with the Euler-error accuracy measures. * Legacy *Master stoch simul* -- variance decomposition and the impulse-response surface (``irf_type``, ``irf_periods``, ``irf_shock_sign``, generalized IRFs, anticipation horizons). * Legacy *Resimulation and counterfactuals* -- the ``+resim_bridge`` surface for Viterbi / FFBS regime histories, counterfactual shock kills, Shapley shock decomposition. * Legacy *Plotting tools* -- ``quick_irfs``, ``fanchart``, ``plot_fanchart``, and the multi-panel layouts.